<script>
import { computed, ref, defineComponent, watch } from "vue";

export default defineComponent({
    name: "AnimatedNumber",
    props: {
        value: {
            type: [Number, String],
            required: true,
        },
        duration: {
            type: Number,
            default: 1000,
        },
        prefix: {
            type: String,
            default: "",
        },
        color: {
            type: String,
            default: "#333",
        },
        suffix: {
            type: String,
            default: "",
        },
        decimals: {
            type: Number,
            default: 2,
        },
        useGrouping: {
            type: Boolean,
            default: true,
        },
        size: {
            type: String,
            default: 'large',
            validator: (val) => ['large', 'medium', 'small'].includes(val)
        },
        // 添加数字表示方法的prop
        notation: {
            type: String,
            default: 'chinese',
            validator: (val) => ['none', 'scientific', 'compact', 'chinese'].includes(val)
        },
        // 科学计数法指数显示方式
        useUnicodeSuperscript: {
            type: Boolean,
            default: true,
        }
    },
    emits: ["end"],
    setup(props, { emit }) {
        const currentValue = ref(0);

        // 定义数字转换为科学计数法的函数
        const toScientificNotation = (value, decimals) => {
            if (Math.abs(value) < 1e5) return Number(value).toFixed(decimals);

            const exponent = Math.floor(Math.log10(Math.abs(value)));
            const coefficient = value / Math.pow(10, exponent);
            const formattedCoefficient = coefficient.toFixed(decimals);

            if (props.useUnicodeSuperscript) {
                // 使用Unicode上标字符
                const superscripts = {
                    '0': '⁰', '1': '¹', '2': '²', '3': '³', '4': '⁴',
                    '5': '⁵', '6': '⁶', '7': '⁷', '8': '⁸', '9': '⁹',
                    '+': '⁺', '-': '⁻'
                };
                const exponentStr = exponent.toString().split('').map(char => superscripts[char] || char).join('');
                return `${formattedCoefficient} × 10${exponentStr}`;
            } else {
                return `${formattedCoefficient} × 10^${exponent}`;
            }
        };

        // 定义数字转换为千分位缩写的函数
        const toCompactNotation = (value, decimals) => {
            const absValue = Math.abs(value);
            const sign = value < 0 ? '-' : '';

            if (absValue < 1e3) return sign + absValue.toFixed(decimals);
            if (absValue < 1e6) return sign + (absValue / 1e3).toFixed(decimals) + 'K';
            if (absValue < 1e9) return sign + (absValue / 1e6).toFixed(decimals) + 'M';
            if (absValue < 1e12) return sign + (absValue / 1e9).toFixed(decimals) + 'B';
            return sign + (absValue / 1e12).toFixed(decimals) + 'T';
        };

        // 定义数字转换为中文计数单位的函数
        const toChineseNotation = (value, decimals) => {
            const absValue = Math.abs(value);
            const sign = value < 0 ? '-' : '';

            if (absValue < 1e4) return sign + absValue.toFixed(decimals);
            if (absValue < 1e8) return sign + (absValue / 1e4).toFixed(decimals) + '万';
            if (absValue < 1e12) return sign + (absValue / 1e8).toFixed(decimals) + '亿';
            return sign + (absValue / 1e12).toFixed(decimals) + '兆';
        };

        const formattedValue = computed(() => {
            const numValue = Number(currentValue.value);

            // 根据notation prop选择不同的数字表示方法
            if (props.notation === 'scientific') {
                const formatted = toScientificNotation(numValue, 2);
                // 如果数字没有被格式化（即没有使用科学计数法），则使用decimals
                return formatted.includes('× 10') ? formatted : numValue.toFixed(props.decimals);
            } else if (props.notation === 'compact') {
                const formatted = toCompactNotation(numValue, 2);
                // 如果数字没有被格式化（即没有使用K、M等缩写），则使用decimals
                return /[KMBT]$/.test(formatted) ? formatted : numValue.toFixed(props.decimals);
            } else if (props.notation === 'chinese') {
                const formatted = toChineseNotation(numValue, 2);
                // 如果数字没有被格式化（即没有使用万、亿等中文单位），则使用decimals
                return /[万亿兆]$/.test(formatted) ? formatted : numValue.toFixed(props.decimals);
            } else {
                // 默认使用标准数字格式化
                const value = numValue.toFixed(props.decimals);
                if (!props.useGrouping) {
                    return value;
                }
                const [integerPart, decimalPart] = value.split('.');
                const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
                return decimalPart ? `${formattedInteger}.${decimalPart}` : formattedInteger;
            }
        });

        const sizeClass = computed(() => {
            if (props.size === 'large') return 'size-large';
            if (props.size === 'medium') return 'size-medium';
            return 'size-small';
        });

        // 添加style计算属性来控制颜色
        const numberStyle = computed(() => ({
            color: props.color
        }));

        const animate = (endValue) => {
            const startValue = currentValue.value;
            const startTime = performance.now();
            let animationFrameId;

            const cubicBezier = (x1, y1, x2, y2, t) => {
                const cx = 3 * x1;
                const bx = 3 * (x2 - x1) - cx;
                const ax = 1 - cx - bx;

                const cy = 3 * y1;
                const by = 3 * (y2 - y1) - cy;
                const ay = 1 - cy - by;

                const sampleCurveY = (t) => {
                    return ((ay * t + by) * t + cy) * t;
                };

                return sampleCurveY(t);
            };

            const updateValue = (currentTime) => {
                const elapsed = currentTime - startTime;

                if (elapsed < props.duration) {
                    const progress = elapsed / props.duration;
                    const easeProgress = cubicBezier(0.25, 0.1, 0.25, 1.0, progress);

                    currentValue.value = startValue + (endValue - startValue) * easeProgress;
                    animationFrameId = requestAnimationFrame(updateValue);
                } else {
                    currentValue.value = endValue;
                    emit("end");
                }
            };

            if (animationFrameId) {
                cancelAnimationFrame(animationFrameId);
            }
            animationFrameId = requestAnimationFrame(updateValue);
        };

        watch(
            () => props.value,
            (newValue) => {
                if (newValue !== undefined && newValue !== null) {
                    animate(Number(newValue));
                }
            },
            { immediate: true }
        );

        return {
            formattedValue,
            sizeClass,
            numberStyle // 返回样式对象
        };
    },
});
</script>

<template>
    <div class="animated-number">
        <span v-if="prefix" class="prefix">{{ prefix }}</span>
        <el-tooltip effect="dark" :content="'' + value">
            <span class="value number-display" :class="sizeClass" :style="numberStyle">{{ formattedValue }}</span>
        </el-tooltip>
        <span v-if="suffix" class="suffix">{{ suffix }}</span>
    </div>
</template>

<style scoped>
.animated-number {
    display: inline-flex;
    align-items: baseline;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    gap: 4px;
}

.prefix,
.suffix {
    font-size: 0.8rem;
    opacity: 0.8;
}

.value {
    transition: color 0.3s ease;
}

/* 根据size属性动态控制字号 */
.size-large {
    font-size: var(--font-size-extra-large);
    /* 24px */
    font-weight: var(--font-weight-primary);
}

.size-medium {
    font-size: var(--font-size-large);
    /* 16px */
    font-weight: var(--font-weight-primary);
}

.size-small {
    font-size: var(--font-size-small);
    /* 14px */
    font-weight: var(--font-weight-regular);
}
</style>